S3C2410下WinCE6.0的啟動(dòng)過(guò)程詳解
OAL(OEM Adaptation Layer)即OEM適配層,它的主要作用是在移植WinCE到新的硬件平臺(tái)時(shí)減少操作系統(tǒng)的修改,通俗的說(shuō)就是為WinCE操作系統(tǒng)抹平MCU的差異,使其能很方便的在不同MCU上運(yùn)行。所以,OAL包括了和系統(tǒng)硬件通訊的最底層代碼。內(nèi)核則通過(guò)OAL跟硬件進(jìn)行交互。邏輯上,OAL是介于CE內(nèi)核和設(shè)備硬件之間的一個(gè)代碼層,是一個(gè)抽象的概念。物理上,OAL和其他一些庫(kù)一起鏈接成可執(zhí)行文件,在WinCE6.0中對(duì)應(yīng)的文件是OAL.exe,這是OAL的客觀存在。WinCE6.0中的OAL跟先前的OAL比,是有一些變化的,它從內(nèi)核中分離出來(lái)成為OAL.exe,而內(nèi)核則變成了Kernel.dll。這樣做的好處是可以單獨(dú)升級(jí)OAL。但整體的OAL結(jié)構(gòu)并沒(méi)有改變,OEM函數(shù)保持一致,OAL和Kernel的接口由共享結(jié)構(gòu)NKGLOBAL實(shí)現(xiàn)。這一部分的具體內(nèi)容下一篇再做介紹。下圖所示為WinCE6.0的OAL設(shè)計(jì)。
在移植WinCE到新的硬件平臺(tái)時(shí),創(chuàng)建OAL是最復(fù)雜的任務(wù)之一。一般來(lái)說(shuō),最簡(jiǎn)單的方法是拷貝一個(gè)跟新的硬件平臺(tái)類(lèi)似的且成熟的OAL,然后根據(jù)硬件的不同進(jìn)行修改,使其滿足目標(biāo)硬件的特定要求。這里不展開(kāi)說(shuō)明,回頭再單獨(dú)整理。
從EBOOT到OAL.exe的跳轉(zhuǎn)是從OEMLaunch()開(kāi)始的,函數(shù)OEMLaunch()中調(diào)用Launch(dwPhysLaunchAddr),它的實(shí)現(xiàn)代碼如下:
Code
LEAF_ENTRYLaunch
ldrr2,=PhysicalStart
ldrr3,=(VIR_RAM_START-PHY_RAM_START)
subr2,r2,r3
movr1,#0x0070;DisableMMU
mcrp15,0,r1,c1,c0,0
nop
movpc,r2;JumptoPStart
nop
;MMU&cachesnowdisabled.
PhysicalStart
movr2,#0
mcrp15,0,r2,c8,c7,0;FlushtheTLB
movpc,r0;Jumptoprogramwearelaunching.
函數(shù)Launch()的參數(shù)為物理地址,因?yàn)樵谔D(zhuǎn)之前已將MMU關(guān)閉。該地址可通過(guò)VIEWBIN來(lái)查看,如下圖所示:
如何確定這個(gè)地址對(duì)應(yīng)的是NK.bin中的哪一個(gè)文件呢,先前說(shuō)是OAL.exe,證據(jù)何在。在PB6.0中增加了瀏覽NK.bin的功能,我們可以利用此功能查看NK.bin的詳細(xì)情況,如下圖所示:
從上圖中可以看出0x80205394處對(duì)應(yīng)的是NK.exe,而這里的NK.exe即為OAL.exe。
至此,我們已經(jīng)知道EBOOT是如何跳轉(zhuǎn)到OAL.exe中的了。接下來(lái)繼續(xù)看OAL.exe的執(zhí)行過(guò)程。
OAL的啟動(dòng)代碼如下:
LEAF_ENTRYStartUp
;ComputetheOEMAddressTablesphysicaladdressand
;loaditintor0.KernelStartexpectsr0tocontain
;thephysicaladdressofthistable.TheMMUisnt
;turnedonuntilwellintoKernelStart.
addr0,pc,#g_oalAddressTable-(.+8)
blKernelStart
OAL的啟動(dòng)代碼和EBOOT的啟動(dòng)代碼經(jīng)常復(fù)用,但為了代碼的簡(jiǎn)潔,最好還是分開(kāi)實(shí)現(xiàn),而且在EBOOT中如果已經(jīng)初始化了相關(guān)硬件,那么OAL的啟動(dòng)代碼就可以省去那部分工作,可以很簡(jiǎn)練,如上面的代碼所示。
可以看出,OAL的啟動(dòng)代碼又調(diào)用了函數(shù)KernelStart(),而這個(gè)函數(shù)是在文件C:WINCE600PRIVATEWINCEOSCOREOSNKLDRARMarmstart.s中實(shí)現(xiàn)的,代碼如下:
LEAF_ENTRYKernelStart
movr11,r0;(r11)=&OEMAddressTable(savepointer)
;figureoutthevirtualaddressofOEMAddressTable
movr1,r11;(r1)=&OEMAddressTable(2ndargumenttoVaFromPa)
blVaFromPa
movr6,r0;(r6)=VAofOEMAddressTable
;convertbaseofPTstoPhysicaladdress
ldrr4,=PTs;(r4)=virtualaddressofFirstPT
movr0,r4;(r0)=virtualaddressofFirstPT
movr1,r11;(r1)=&OEMAddressTable(2ndargumenttoPaFromVa)
blPaFromVa
movr10,r0;(r10)=ptrtoFirstPT(physical)
;Zerooutpagetables&kerneldatapage
movr0,#0;(r0-r3)=0stostore
movr1,#0
movr2,#0
movr3,#0
movr4,r10;(r4)=firstaddresstoclear
addr5,r10,#KDEnd-PTs;(r5)=lastaddress+1
18stmiar4!,{r0-r3}
stmiar4!,{r0-r3}
cmpr4,r5
blo%B18
;readthearchitectureinformation
blGetCpuId
movr5,r0LSR#16;r5>>=16
andr5,r5,#0x0000000f;r5&=0x0000000f==architectureid
;Setup2ndlevelpagetabletomapthehighmemoryareawhichcontainsthe
;firstlevelpagetable,2ndlevelpagetables,kerneldatapage,etc.
;(r5)=architectureid
addr4,r10,#HighPT-PTs;(r4)=ptrtohighpagetable
cmpr5,#ARMv6;v6orlater?
;ARMV6_MMU
orrger0,r10,#PTL2_KRW+PTL2_SMALL_PAGE+ARMV6_MMU_PTL2_SMALL_XN
;(r0)=PTEfor4K,kr/wu-/-page,uncachedunbuffered,nonexecutable
;PREARMV6_MMU
orrltr0,r10,#PTL2_KRW+(PTL2_KRW<<2)+(PTL2_KRW<<4)+(PTL2_KRW<<6)
;NeedtoreplicateAPbitsintoall4fields
orrltr0,r0,#PTL2_SMALL_PAGE+PREARMV6_MMU_PTL2_SMALL_XN
;(r0)=PTEfor4K,kr/wu-/-page,uncachedunbuffered,nonexecutable
strr0,[r4,#0xD0*4];storetheentryinto4slotstomap16Kofprimarypagetable
addr0,r0,#0x1000;steponthephysicaladdress
strr0,[r4,#0xD1*4]
addr0,r0,#0x1000;steponthephysicaladdress
strr0,[r4,#0xD2*4]
addr0,r0,#0x1000;steponthephysicaladdress
strr0,[r4,#0xD3*4]
addr8,r10,#ExceptionVectors-PTs;(r8)=ptrtovectorpage
orrr0,r8,#PTL2_SMALL_PAGE;constructthePTE(C=B=0)
;;Theexceptionstacksandthevectorsaremappedasasinglekr/wpage.
;;Anyalternativewillusemorephysicalmemory.
;;Multiplemappingsdontprovideanyrealprotection:ifthevectorswereinar/opage,
;;theycouldstillbecorruptedviathekr/wsettingrequiredforthestacks.
cmpr5,#ARMv6;v6orlater?
;ARMV6_MMU
orrger0,r0,#PTL2_KRW
;PREARMV6_MMU
orrltr0,r0,#PTL2_KRW+(PTL2_KRW<<2)+(PTL2_KRW<<4)+(PTL2_KRW<<6)
;NeedtoreplicateAPbitsintoall4fieldsforpre-V6MMU
strr0,[r4,#0xF0*4];storeentryforexceptionstacksandvectors
;other3entriesnowunused
addr9,r10,#KPage-PTs;(r9)=ptrtokdatapage
orrr0,r9,#PTL2_SMALL_PAGE;(r0)=PTEfor4K(C=B=0)
;ARMV6_MMU(conditioncodesstillset)
orrger0,r0,#PTL2_KRW_URO;Nosubpageaccesscontrol,sowemustsetthisalltokr/w+ur/o
;PREARMV6_MMU
orrltr0,r0,#(PTL2_KRW<<0)+(PTL2_KRW<<2)+(PTL2_KRW_URO<<4)
;(r0)=setpermskr/wkr/wkr/w+ur/or/o
strr0,[r4,#0xFC*4];storeentryforkerneldatapage
orrr0,r4,#PTL1_2Y_TABLE;(r0)=1stlevelPTEforhighmemorysection
addr1,r10,#0x4000
strr0,[r1,#-4];storePTEinlastslotof1stleveltable
;Fillinfirstlevelpagetableentriestocreate"staticallymapped"regions
;fromthecontentsoftheOEMAddressTablearray.
;
;(r5)=architectureid
;(r9)=ptrtoKDatapage
;(r10)=ptrto1stlevelpagetable
;(r11)=ptrtoOEMAddressTablearray
addr10,r10,#0x2000;(r10)=ptrto1stPTEfor"unmappedspace"
movr0,#PTL1_SECTION
orrr0,r0,#PTL1_KRW;(r0)=PTEfor0:1MB(C=B=0,kernelr/w)
20movr1,r11;(r1)=ptrtoOEMAddressTablearray(physical)
25ldrr2,[r1],#4;(r2)=virtualaddresstomapBankat
ldrr3,[r1],#4;(r3)=physicaladdresstomapfrom
ldrr4,[r1],#4;(r4)=numMBtomap
cmpr4,#0;Endoftable?
beq%F29
ldrr12,=0x1FF00000
andr2,r2,r12;VAneeds512MB,1MBaligned.
ldrr12,=0xFFF00000
andr3,r3,r12;PAneeds4GB,1MBaligned.
addr2,r10,r2,LSR#18
addr0,r0,r3;(r0)=PTEfornextphysicalpage
28strr0,[r2],#4
addr0,r0,#0x00100000;(r0)=PTEfornextphysicalpage
subr4,r4,#1;DecrementnumberofMBleft
cmpr4,#0
bne%B28;MapnextMB
bicr0,r0,#0xF0000000;ClearSectionBaseAddressField
bicr0,r0,#0x0FF00000;ClearSectionBaseAddressField
b%B25;Getnextelement
29
subr10,r10,#0x2000;(r10)=restoreaddressof1stlevelpagetable
;Theminimalpagemappingsaresetup.InitializetheMMUandturniton.
;therearesomeCPUswithpipelineissuesthatrequiresidentitymappingbeforeturningonMMU.
;WellcreateanidentitymappingfortheaddresswelljumptowhenturningonMMUonandremove
;themappingafterweturnonMMUandrunningonVirtualaddress.
ldrr12,=0xFFF00000;(r12)=maskforsectionbits
andr1,pc,r12;physicaladdressofwhereweare
;NOTE:weassumethattheKernelStartfunctionneverspamacross1Mboundary.
orrr0,r1,#PTL1_SECTION
orrr0,r0,#PTL1_KRW;(r0)=PTEfor1Mforcurrentphysicaladdress,C=B=0,kernelr/w
addr7,r10,r1,LSR#18;(r7)=1stlevelPTentryfortheidentitymap
ldrr8,[r7];(r8)=savedcontentofthe1st-levelPT
strr0,[r7];createtheidentitymap
movr1,#1
mtc15r1,c3;Setupaccesstodomain0andclearother
mtc15r10,c2;setuptranslationbase(physicalof1stlevelPT)
movr0,#0
mcrp15,0,r0,c8,c7,0;FlushtheI&DTLBs
mfc15r1,c1
orrr1,r1,#0x007F;changedtoread-mod-writeforARM920Enable:MMU,Align,DCache,WriteBuffer
cmpr5,#ARMv6;r5stillset
;ARMV6_MMU
orrger1,r1,#0x3000;vectoradjust,ICache
orrger1,r1,#1<<23;V6-formatpagetables
orrger1,r1,#ARMV6_U_BIT;V6-setUbit,letAbitcontrolunalignmentsupport
;PREARMV6_MMU
orrltr1,r1,#0x3200;vectoradjust,ICache,ROMprotection
ldrr0,VirtualStart
cmpr0,#0;makesurenostallon"movpc,r0"below
mtc15r1,c1;enabletheMMU&Caches
movpc,r0;&jumptonewvirtualaddress
nop
;MMU&cachesnowenabled.
;
;(r10)=physcialaddressof1stlevelpagetable
;(r7)=entryin1stlevelPTforidentitymap
;(r8)=saved1stlevelPTsaveat(r7)
VStartldrr2,=FirstPT;(r2)=VAof1stlevelPT
subr7,r7,r10;(r7)=offsetinto1st-levelPT
strr8,[r2,r7];restorethetemporaryidentitymap
mcrp15,0,r0,c8,c7,0;FlushtheI&DTLBs
;
;setupstackforeachmodes:currentmode=supervisormode
;
ldrsp,=KStack
addr4,sp,#KData-KStack;(r4)=ptrtoKDataStruct
;setupABORTstack
movr1,#ABORT_MODE:OR:0xC0
msrcpsr_c,r1;switchtoAbortModew/IRQsdisabled
addsp,r4,#AbortStack-KData
;setupIRQstack
movr2,#IRQ_MODE:OR:0xC0
msrcpsr_c,r2;switchtoIRQModew/IRQsdisabled
addsp,r4,#IntStack-KData
;setupFIQstack
movr3,#FIQ_MODE:OR:0xC0
msrcpsr_c,r3;switchtoFIQModew/IRQsdisabled
addsp,r4,#FIQStack-KData
;setupUNDEFstack
movr3,#UNDEF_MODE:OR:0xC0
msrcpsr_c,r3;switchtoUndefinedModew/IRQsdisabled
movsp,r4;(sp_undef)=&KData
;switchbacktoSupervisormode
movr0,#SVC_MODE:OR:0xC0
msrcpsr_c,r0;switchtoSupervisorModew/IRQsdisabled
ldrsp,=KStack
;continueinitializationinC
addr0,sp,#KData-KStack;(r0)=ptrtoKDataStruct
strr6,[r0,#pAddrMap];storeVAofOEMAddressTableinKData
blARMInit;callCfunctiontoperformtherestofinitializations
;uponreturn,(r0)=entrypointofkernel.dll
movr12,r0
ldrr0,=KData
movpc,r12;jumptoentryofkernel.dll
從上面的代碼可以看出,KernelStart()通過(guò)OEMAddressTable初始化了MMU,然后通過(guò)調(diào)用函數(shù)ARMInit()獲得kernel.dll的入口點(diǎn),最后跳轉(zhuǎn)到kernel.dll的入口點(diǎn)處。
為了找到Kernel.dll的入口點(diǎn),用IDA反匯編kernel.dll文件,可以看到,Kernel.dll的入口點(diǎn)為NKStartup。
NKStartup()的實(shí)現(xiàn)在文件C:WINCE600PRIVATEWINCEOSCOREOSNKKERNELARM mdarm.c中,代碼如下:
//
//NKStartup-entrypointofkernel.dll.
//
//NKLoadersetuponlytheminimalmappings,whichincludesARMHigharea,andthecachedstaticmappingarea,
//with*EVERYTHINGUNCACHED*.Interruptvectorsarenotsetupeither.So,theinitsequencereqiures:
//(1)pickupdatapassedfromnkloader
//(2)Findentrypointofoal,exchangeglobals,findoutthecachemode.
//(3)fillintherestofstaticmappedarea(0xa0000000-0xbfffffff),PSLfaultingaddress,interruptvectors,
//modstacks,etc.Then,changethecachedstaticmappingareatousecache,andflushI&DTLB.
//(4)continuenormalloadingofkernel(findKITLdll,callOEMInitDebugSerial,etc.)
//
voidNKStartup(structKDataStruct*pKData)
{
PFN_OEMInitGlobalspfnInitGlob;
PFN_DllMainpfnKitlEntry;
DWORDdwCpuId=GetCpuId();
//(1)pickupargumentsfromthenkloader
g_pKData=pKData;
pTOC=(constROMHDR*)pKData->dwTOCAddr;
g_pOEMAddressTable=(PADDRMAP)pKData->pAddrMap;
/*getarchitectureidandupdatepageprotectionattributes*/
pKData->dwArchitectureId=(dwCpuId>>16)&0xf;
if(pKData->dwArchitectureId>=ARMArchitectureV6){
//v6orlater
pKData->dwProtMask=PG_V6_PROTECTION;
pKData->dwRead=PG_V6_PROT_READ;
pKData->dwWrite=PG_V6_PROT_WRITE;
pKData->dwKrwUro=PG_V6_PROT_URO_KRW;
pKData->dwKrwUno=PG_V6_PROT_UNO_KRW;
}else{
//pre-v6
pKData->dwProtMask=PG_V4_PROTECTION;
pKData->dwRead=PG_V4_PROT_READ;
pKData->dwWrite=PG_V4_PROT_WRITE;
pKData->dwKrwUro=PG_V4_PROT_URO_KRW;
pKData->dwKrwUno=PG_V4_PROT_UNO_KRW;
}
//initializenkglobals
FirstROM.pTOC=(ROMHDR*)pTOC;
FirstROM.pNext=0;
ROMChain=&FirstROM;
KInfoTable[KINX_PTOC]=(long)pTOC;
KInfoTable[KINX_PAGESIZE]=VM_PAGE_SIZE;
g_ppdirNK=(PPAGEDIRECTORY)&ArmHigh->firstPT[0];
pKData->pNk=g_pNKGlobal;
//(2)findentryofoal
pfnInitGlob=(PFN_OEMInitGlobals)pKData->dwOEMInitGlobalsAddr;
//nocheckinghere,ifOALentrypointdoesntexist,wecantcontinue
g_pOemGlobal=pfnInitGlob(g_pNKGlobal);
g_pOemGlobal->dwMainMemoryEndAddress=pTOC->ulRAMEnd;
pKData->pOem=g_pOemGlobal;
//setupglobals
pVMProc=g_pprcNK;
pActvProc=g_pprcNK;
g_pNKGlobal->pfnWriteDebugString=g_pOemGlobal->pfnWriteDebugString;
//(3)setupvectors,UCmappings,modestacks,etc.
ARMSetup();
//
//cacheisenabledfromhereon
//
//(4)commonstartupcode.
//trytoloadKITLifexist
if((pfnKitlEntry=(PFN_DllMain)g_pOemGlobal->pfnKITLGlobalInit)||
(pfnKitlEntry=(PFN_DllMain)FindROMDllEntry(pTOC,KITLDLL))){
(*pfnKitlEntry)(NULL,DLL_PROCESS_ATTACH,(DWORD)NKKernelLibIoControl);
}
#ifdefDEBUG
CurMSec=dwPrevReschedTime=(DWORD)-200000;//~3minutesbeforewrap
#endif
OEMInitDebugSerial();
//debugchkonlyworksafterwehavesomethingtoprintto.
DEBUGCHK(pKData==(structKDataStruct*)PUserKData);
DEBUGCHK(pKData==&ArmHigh->kdata);
OEMWriteDebugString((LPWSTR)NKSignon);
/*Copyinterlockedapicodeintothekpage*/
DEBUGCHK(sizeof(structKDataStruct)<=FIRST_INTERLOCK);
DEBUGCHK((InterlockedEnd-InterlockedAPIs)+FIRST_INTERLOCK<=0x400);
memcpy((char*)g_pKData+FIRST_INTERLOCK,InterlockedAPIs,InterlockedEnd-InterlockedAPIs);
/*setupprocessorversioninformation*/
CEProcessorType=(dwCpuId>>4)&0xFFF;
CEProcessorLevel=4;
CEProcessorRevision=(WORD)dwCpuId&0x0f;
CEInstructionSet=PROCESSOR_ARM_V4I_INSTRUCTION;
RETAILMSG(1,(L"ProcessorType=%4.4xRevision=%drn",CEProcessorType,CEProcessorRevision));
RETAILMSG(1,(L"OEMAddressTable=%8.8lxrn",g_pOEMAddressTable));
OEMInit();//initializefirmware
//flushI&DTLB
OEMCacheRangeFlush(NULL,0,CACHE_SYNC_FLUSH_TLB);
KernelFindMemory();
DEBUGMSG(1,(TEXT("NKStartupdone,startingupkernel.rn")));
KernelStart();
//neverreturned
DEBUGCHK(0);
}
NKStartup()的代碼就不多解釋了,注釋已經(jīng)很詳細(xì)。該函數(shù)的最后又調(diào)用了KernelStart ()函數(shù)。注意這里的KernelStart()跟上面曾提到的KernelStart()是不一樣的。這里KernelStart()的實(shí)現(xiàn)在文件C:WINCE600PRIVATEWINCEOSCOREOSNKKERNELARMarmtrap.s中,代碼和反匯編的對(duì)比如下圖所示。
可以看到,這里調(diào)用了KernelInit()和FirstSchedule()這兩個(gè)函數(shù)。先說(shuō)FirstSchedule(),它開(kāi)始了WinCE6.0的第一個(gè)調(diào)度。它的實(shí)現(xiàn)跟KernelStart()在同一文件中,而實(shí)現(xiàn)代碼跟WinCE5.0中完全一樣。接下來(lái),我們繼續(xù)跟蹤KernelInit()函數(shù),其實(shí)現(xiàn)在文件C:WINCE600PRIVATEWINCEOSCOREOSNKKERNELnkinit.c中,代碼如下:
//------------------------------------------------------------------------------
//KernelInit-Kernelinitializationbeforeschedulingthe1stthread
//------------------------------------------------------------------------------
voidKernelInit(void)
{
#ifdefDEBUG
g_pNKGlobal->pfnWriteDebugString(TEXT("WindowsCEKernelInitrn"));
#endif
APICallInit();//setupAPIset
HeapInit();//setupkernelheap
InitMemoryPool();//setupphysicalmemory
PROCInit();//initializeprocess
VMInit(g_pprcNK);//setupVMforkernel
THRDInit();//initializethreads
MapfileInit();
#ifdefDEBUG
g_pNKGlobal->pfnWriteDebugString(TEXT("Schedulingthefirstthread.rn"));
#endif
}
這段代碼跟WinCE5.0中的結(jié)構(gòu)基本一致,但實(shí)際上有很大的不同。跟WinCE6.0啟動(dòng)最緊密的函數(shù)是THRDInit (),這之前都是做相應(yīng)的初始化。THRDInit ()的實(shí)現(xiàn)在文件C:WINCE600PRIVATEWINCEOSCOREOSNKKERNELthread.c中,代碼如下:
//------------------------------------------------------------------------------
//THRDInit-initializethreadhandling(calledatsystemstartup)
//------------------------------------------------------------------------------
voidTHRDInit(void)
{
LPBYTEpStack;
DEBUGLOG(1,g_pprcNK);
//dontallowthreadcreateonememorydropbelow1%available
if(g_cMinPageThrdCreate
}
//mapW32threadpriorityifOEMchooseto
if(g_pOemGlobal->pfnMapW32Priority){
BYTEprioMap[MAX_WIN32_PRIORITY_LEVELS];
inti;
memcpy(prioMap,W32PrioMap,sizeof(prioMap));
g_pOemGlobal->pfnMapW32Priority(MAX_WIN32_PRIORITY_LEVELS,prioMap);
//validatethethepriorityismono-increase
for(i=0;i
break;
}
DEBUGMSG((MAX_WIN32_PRIORITY_LEVELS-1)!=i,(L"ProcInit:InvalidprioritymapprovidedbyOEM,Ignored!rn"));
if((MAX_WIN32_PRIORITY_LEVELS-1)==i){
memcpy(W32PrioMap,prioMap,sizeof(prioMap));
}
}
//allocatememoryforthe1stthread
pCurThread=AllocMem(HEAP_THREAD);
DEBUGCHK(pCurThread);
dwCurThId=(DWORD)HNDLCreateHandle(&cinfThread,pCurThread,g_pprcNK)&~1;
DEBUGCHK(dwCurThId);
InitThreadStruct(pCurThread,(HANDLE)dwCurThId,g_pprcNK,THREAD_RT_PRIORITY_ABOVE_NORMAL);
if(g_pOemGlobal->cbCoProcRegSize){
DEBUGCHK(g_pOemGlobal->pfnInitCoProcRegs);
DEBUGCHK(g_pOemGlobal->pfnSaveCoProcRegs);
DEBUGCHK(g_pOemGlobal->pfnRestoreCoProcRegs);
//checkthedebugregisterrelatedvalues.
if(g_pOemGlobal->cbCoProcRegSize>MAX_COPROCREGSIZE){
g_pOemGlobal->cbCoProcRegSize=g_pOemGlobal->fSaveCoProcReg=0;
}else{
PNAMEpTmp=AllocName(g_pOemGlobal->cbCoProcRegSize);
DEBUGCHK(pTmp);
g_dwCoProcPool=pTmp->wPool;
FreeName(pTmp);
}
}else{
g_pOemGlobal->fSaveCoProcReg=FALSE;
}
DEBUGMSG(ZONE_SCHEDULE,(TEXT("cbCoProcRegSize=%drn"),g_pOemGlobal->cbCoProcRegSize));
AddToDListHead(&g_pprcNK->thrdList,&pCurThread->thLink);
g_pprcNK->wThrdCnt++;
#ifdefSHx
SetCPUGlobals();
OEMCacheRangeFlush(0,0,CACHE_SYNC_ALL);
#endif
if(!OpenExecutable(NULL,TEXT("NK.EXE"),&g_pprcNK->oe,TOKEN_SYSTEM,NULL,0)){
LoadE32(&g_pprcNK->oe,&g_pprcNK->e32,0,0,0);
g_pprcNK->BasePtr=(LPVOID)g_pprcNK->e32.e32_vbase;
UpdateKmodVSize(&g_pprcNK->oe,&g_pprcNK->e32);
}
//create/setupstack
pStack=VMCreateStack(g_pprcNK,KRN_STACK_SIZE);
pCurThread->dwOrigBase=(DWORD)pStack;
pCurThread->dwOrigStkSize=KRN_STACK_SIZE;
pCurThread->tlsSecure=pCurThread->tlsNonSecure=pCurThread->tlsPtr=TLSPTR(pStack,KRN_STACK_SIZE);
pCurThread->hTok=TOKEN_SYSTEM;
//Saveoffthethreadsprogramcounterforgettingitsnamelater.
pCurThread->dwStartAddr=(DWORD)SystemStartupFunc;
MDSetupThread(pCurThread,(LPVOID)SystemStartupFunc,0,TH_KMODE,0);
CELOG_ThreadCreate(pCurThread,g_pprcNK,NULL);
MakeRun(pCurThread);
DEBUGMSG(ZONE_SCHEDULE,(TEXT("Scheduler:Createdmasterthread%8.8lxrn"),pCurThread));
}
可以看到,這里開(kāi)始了一個(gè)線程,線程處理函數(shù)為SystemStartupFunc(),其實(shí)現(xiàn)在文件C:WINCE600PRIVATEWINCEOSCOREOSNKKERNELschedule.c,實(shí)現(xiàn)代碼如下:
//------------------------------------------------------------------------------
void
SystemStartupFunc(
ulongparam
)
{
HANDLEhTh;
//recordPendEventaddressforSetInterruptEvent
KInfoTable[KINX_PENDEVENTS]=(DWORD)&PendEvents1;
KernelInit2();
//adjustalarmresolutionifititsnotinbound
if(g_pOemGlobal->dwAlarmResolution
elseif(g_pOemGlobal->dwAlarmResolution>MAX_NKALARMRESOLUTION_MSEC)
g_pOemGlobal->dwAlarmResolution=MAX_NKALARMRESOLUTION_MSEC;
VERIFY(LoaderInit());
//initializethecompiler/GScookie-thismusthappenbeforeotherthreads
//startrunning
__security_init_cookie();
PagePoolInit();
//Thiscanonlybedoneaftertheloaderinitialization
LoggerInit();//InitializationforCeLog,profiler,code-coverage,etc.
SysDebugInit();//initializeSystemDebugger(HWDebugstub,Kerneldumpcapture,SWKernelDebugstub)
//dothisnow,sothatwecontinuerunningafterwevecreatedthenewthread
#ifdefSTART_KERNEL_MONITOR_THREAD
hTh=CreateKernelThread(Monitor1,0,THREAD_RT_PRIORITY_ABOVE_NORMAL,0);
HNDLCloseHandle(g_pprcNK,hTh);
#endif
pCleanupThread=pCurThread;
hAlarmThreadWakeup=NKCreateEvent(0,0,0,0);
DEBUGCHK(hAlarmThreadWakeup);
InitializeCriticalSection(&rtccs);
IntrEvents[SYSINTR_RTC_ALARM-SYSINTR_DEVICES]=LockIntrEvt(hAlarmThreadWakeup);
DEBUGCHK(IntrEvents[SYSINTR_RTC_ALARM-SYSINTR_DEVICES]->phdIntr);
//GivetheOEMafinalchancetodoamorefull-featuredinitbeforeany
//appsarestarted
KernelIoctl(IOCTL_HAL_POSTINIT,NULL,0,NULL,0,NULL);
InitMsgQueue();
InitWatchDog();
//createthepowerhandlereventandguardthread
hEvtPwrHndlr=NKCreateEvent(NULL,FALSE,FALSE,NULL);
DEBUGCHK(hEvtPwrHndlr);
hTh=CreateKernelThread(PowerHandlerGuardThrd,NULL,THREAD_PWR_GUARD_PRIORITY,0);
HNDLCloseHandle(g_pprcNK,hTh);
//dirtypageevent,initiallyset
hEvtDirtyPage=NKCreateEvent(NULL,FALSE,TRUE,NULL);
DEBUGCHK(hEvtDirtyPage);
//wedontwanttowasteathreadhere(createaseparateforcleaningdirtypages).
//Instead,RunAppsthreadwillbecome"CleanDirtyPage"threadoncefilesysstarted
hTh=CreateKernelThread(RunApps,0,THREAD_RT_PRIORITY_NORMAL,0);
HNDLCloseHandle(g_pprcNK,hTh);
#defineONE_DAY86400000
while(1){
KCall((PKFN)SetThreadBasePrio,pCurThread,dwNKAlarmThrdPrio);
NKWaitForSingleObject(hAlarmThreadWakeup,ONE_DAY);
NKRefreshKernelAlarm();
PageOutIfNeeded();
}
}
這里創(chuàng)建了一個(gè)內(nèi)核線程,處理函數(shù)為RunApps,繼續(xù)跟蹤RunApps,其實(shí)現(xiàn)在文件C:WINCE600PRIVATEWINCEOSCOREOSNKKERNELkmisc.c中,代碼如下:
DWORDWINAPI
RunApps(
LPVOIDparam
)
{
HMODULEhFilesys;
DEBUGMSG(ZONE_ENTRY,(L"RunAppsstartedrn"));
CELOG_LaunchingFilesys();
hFilesys=(HMODULE)NKLoadLibraryEx(L"filesys.dll",MAKELONG(LOAD_LIBRARY_IN_KERNEL,LLIB_NO_PAGING),NULL);
if(hFilesys){
FARPROCpfnMain=GetProcAddressA(hFilesys,(LPCSTR)2);//WinMainoffilesys
HANDLEhFSReady,hTh;
DEBUGCHK(pfnMain);
hFSReady=NKCreateEvent(NULL,TRUE,FALSE,TEXT("SYSTEM/FSReady"));
hTh=CreateKernelThread((LPTHREAD_START_ROUTINE)pfnMain,hFilesys,THREAD_RT_PRIORITY_NORMAL,0);
DEBUGCHK(hTh&&hFSReady);
HNDLCloseHandle(g_pprcNK,hTh);
//IfpSignalStartedisNULL,wedonthavefilesys(tinykern).Dontbotherwaitingforit.
if(pSignalStarted){
NKWaitForSingleObject(hFSReady,INFINITE);
DEBUGCHK(SystemAPISets[SH_FILESYS_APIS]);
//InitializeMUI-Resourceloader(requiresregistry)
InitMUILanguages();
//Readsystemsettingsfromregistry
InitSystemSettings();
//signalfilesysthatweredone
(*pSignalStarted)(0);
}
HNDLCloseHandle(g_pprcNK,hFSReady);
}else{
RETAILMSG(1,(L"Filesysdoesntexist,noappstartedrn"));
}
//insteadofexiting,weremakethisthreadcleaningdirtypagesinthebackground.
CleanPagesInTheBackground();
//shouldveneverreturned
DEBUGCHK(0);
NKExitThread(0);
return0;
}
呵,沒(méi)想到WinCE6.0的啟動(dòng)過(guò)程竟然這么繁長(zhǎng)。不過(guò),弄清楚這個(gè)啟動(dòng)流程對(duì)于移植BSP相當(dāng)有好處??偨Y(jié)一下整個(gè)過(guò)程,如下圖所示。
本文通過(guò)跟蹤代碼的方式,介紹了WinCE6.0的啟動(dòng)流程。流于表面了一點(diǎn),很多細(xì)節(jié)應(yīng)該進(jìn)一步研究,以后再慢慢看吧。文中有不確切的地方,也請(qǐng)您不吝賜教.
評(píng)論